namespace NHCodeFirst.FluentMapper
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using Enums;
    using HbmModel;
    using Helpers;

    public class MemberMapper<TEntity, TChild>
    {
        private readonly MemberContainer container;
        protected TChild child;

        internal MemberMapper(MemberContainer container)
        {
            this.container = container;
        }

        public TChild Tuplizer<T>(TuplizerEntityMode entityMode)
        {
            string mode;
            switch (entityMode)
            {
                case TuplizerEntityMode.DynamicMap:
                    mode = "dynamic-map";
                    break;
                default:
                    mode = entityMode.ToString().ToLower();
                    break;
            }
            container.Tuplizers.Add(typeof (T).FullName ?? "", mode);
            return child;
        }

        public PropertyMapper Property<TProperty>(Expression<Func<TEntity, TProperty>> selector)
        {
            var propertyName = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(propertyName, PropertyUsage.Property);
            if (!container.Properties.ContainsKey(propertyName))
                container.Properties.Add(propertyName, new Property {Name = propertyName, Column = new Column(), Type = new TypeE()});
            return new PropertyMapper(container.Properties[propertyName]);
        }

        public ComponentMapper<TComponent> Component<TComponent>(Expression<Func<TEntity, TComponent>> selector)
            where TComponent : class
        {
            var name = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(name, PropertyUsage.Component);
            if (!container.Components.ContainsKey(name))
                container.Components.Add(name, new Component {Name = name, Class = typeof (TComponent).FullName + "," + typeof (TComponent).Assembly.GetName().Name});
            return new ComponentMapper<TComponent>(container.Components[name]);
        }

        public AnyMapper<TAny> Any<TAny>(Expression<Func<TEntity,TAny>> selector)
        {
            var name = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(name, PropertyUsage.Any);
            if (!container.Anies.ContainsKey(name))
                container.Anies.Add(name, new Any {Name = name});
            return new AnyMapper<TAny>(container.Anies[name]);
        }

        public OneToOneMapper<TProperty> OneToOne<TProperty>(Expression<Func<TEntity,TProperty>> selector)
        {
            var name = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(name, PropertyUsage.OneToOne);
            if(!container.OneToOnes.ContainsKey(name))
                container.OneToOnes.Add(name, new OneToOne {Name = name, Class = typeof (TProperty).FullName});
            return new OneToOneMapper<TProperty>(container.OneToOnes[name]);
        }

        public OneToManyMapper<TProperty> OneToMany<TProperty>(Expression<Func<TEntity,ICollection<TProperty>>> selector)
        {
            var name = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(name, PropertyUsage.Collection);
            if (!container.Collections.ContainsKey(name))
                container.Collections.Add(name, new Collection
                                                    {
                                                        Name = name,
                                                        Inverse = "true",
                                                        Cascade = "all-delete-orphan",
                                                        OneToMany = new OneToMany
                                                                        {
                                                                            Class = typeof (TProperty).FullName + ","
                                                                                    + typeof (TProperty).Assembly.GetName().Name
                                                                        }
                                                    });
            return new OneToManyMapper<TProperty>(container.Collections[name]);
        }


        public ManyToManyMapper<TProperty> ManyToMany<TProperty>(Expression<Func<TEntity, TProperty>> selector, string table)
        {
            var name = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(name, PropertyUsage.Collection);
            if (!container.Collections.ContainsKey(name))
                container.Collections.Add(name, new Collection
                                                    {
                                                        Name = name,
                                                        Table = table,
                                                        Inverse = "false",
                                                        Lazy = "true",
                                                        Cascade = "none",
                                                        ManyToMany = new ManyToMany {Class = typeof(TProperty).FullName}
                                                    });
            return new ManyToManyMapper<TProperty>(container.Collections[name]);
        }

        public ManyToOneMapper<TEntity, TProperty> ManyToOne<TProperty>(Expression<Func<TEntity,TProperty>> selector)
        {
            var name = new ExpressionHelper().GetPropertyName(selector);
            ChangePropertyUsage(name, PropertyUsage.ManyToOne);
            if (!container.ManyToOnes.ContainsKey(name))
                container.ManyToOnes.Add(name, new ManyToOne {Name = name, Class = typeof (TProperty).FullName});
            return new ManyToOneMapper<TEntity, TProperty>(container.ManyToOnes[name]);
        }

        public TChild Ignore<TProperty>(Expression<Func<TEntity, TProperty>> selector)
        {
            container.Ignored.Add(new ExpressionHelper().GetPropertyName(selector));
            return child;
        }

        private void ChangePropertyUsage(string name, PropertyUsage usage)
        {
            if (!container.Usages.ContainsKey(name))
                container.Usages.Add(name, usage);
            else
                container.Usages[name] = usage;
        }
    }
}